/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import {
  BaseCommand,
  FeatureManager,
  pickedFaceData,
  pickedLineData,
  pickedModelData,
} from '@amcax/base';
import {
  BasePickInputRJSFFormat,
  PickInputRJSFFormat,
} from '@app-cad/common/components/command-form/constants';
import { PickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import { composeFormat } from '@app-cad/common/components/command-form/utils';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import ReverseIconWidget from '@app-cad/common/RJSFWidgets/ReverseIconWidget';
import { getIdOfPickedObj } from '@app-cad/common/utils/render';
import { previewer } from '@app-cad/module';
import { wsApi } from '@app-cad/server';
import { FeaturedCmdDialogParams } from '@app-cad/types';
import { RJSFSchema } from '@rjsf/utils';
import { useAsyncEffect, useDebounce } from 'ahooks';
import i18n from 'i18next';
import { useMemo, useState } from 'react';
import { useDollyCamera } from '../../../../product/commands/common/hooks/useDollyCamera';
import {
  getArrayProperties,
  getArraySettingsFromParams,
  getDefaultArraySettings,
  removeUnusedFieldForArray,
  validateArraySettings,
} from './form-utils';
import { FormData, Params } from './types';

import './index.scss';

function CircularArray({
  title,
  onClose,
  placement,
  props,
}: FeaturedCmdDialogParams) {
  useDollyCamera();

  const [formData, setFormData] = useState<FormData>({
    mode: 'circle',

    base: null,
    axis: null,

    array1: getDefaultArraySettings(),
    reverse: false,

    array2: getDefaultArraySettings(),
    equal: true,
  });
  const { picker } = useTools();

  useAsyncEffect(async () => {
    if (props == null) {
      return;
    }

    const { baseid } = props;
    const {
      mode,
      axis,

      angleMode,
      count,
      angle,
      distance, // for old API compatibility
      reverse = false,

      angleMode1,
      count1,
      angle1,
      distance1, // ditto

      angleMode2,
      count2,
      angle2,
      distance2, // ditto

      equal,
    } = props.params as Params;

    await initEditDependency([baseid, axis]);
    setFormData({
      mode,

      base: picker.generatePickObjByPickID(baseid) as pickedModelData,
      axis: picker.generatePickObjByPickID(axis) as
        | pickedLineData
        | pickedFaceData,

      array1:
        mode === 'circle'
          ? getArraySettingsFromParams(angleMode, count, angle, distance)
          : getArraySettingsFromParams(angleMode1, count1, angle1, distance1),
      reverse,

      array2:
        mode === 'circle'
          ? getDefaultArraySettings()
          : getArraySettingsFromParams(angleMode2, count2, angle2, distance2),
      equal: mode === 'doubleCircle' ? equal : true,
    });
  }, [props]);

  const debouncedFormData = useDebounce(formData, { wait: 300 });
  useAsyncEffect(async () => {
    if (!isFormDataValid()) {
      previewer.clear();
      return;
    }
    await execute(true);
  }, [debouncedFormData]);

  const isFormDataValid = () => {
    const {
      mode,
      base,
      axis,

      array1,
      reverse,

      array2,
      equal,
    } = formData;

    const commonFieldsValid =
      base != null && axis != null && validateArraySettings(array1);

    if (mode === 'circle') {
      return commonFieldsValid && reverse != null;
    } else {
      return commonFieldsValid && (equal || validateArraySettings(array2));
    }
  };

  const finishValid = useMemo(isFormDataValid, [formData]);

  const execute = async (preview = false) => {
    const {
      mode,
      base,
      axis,

      reverse,

      array1: {
        angleMode: angleMode1,
        count: count1,
        angle: angle1,
        singleAngle: singleAngle1,
      },
      array2: {
        angleMode: angleMode2,
        count: count2,
        angle: angle2,
        singleAngle: singleAngle2,
      },

      equal,
    } = formData;
    const baseID = getIdOfPickedObj(base);
    const params = {
      mode: mode,
      axis: getIdOfPickedObj(axis),
    };
    let dynamicParams: Partial<Params>;

    if (mode === 'circle') {
      dynamicParams = {
        angleMode: angleMode1,
        count: count1,
        angle: angleMode1 === 'angle' ? angle1 : singleAngle1,
        reverse,
      };
    } else {
      dynamicParams = {
        angleMode1,
        count1,
        angle1: angleMode1 === 'angle' ? angle1 : singleAngle1,

        angleMode2,
        count2,
        angle2: angleMode2 === 'angle' ? angle2 : singleAngle2,

        equal,
      };

      if (equal) {
        delete dynamicParams.angleMode2;
        delete dynamicParams.count2;
        delete dynamicParams.angle2;
      }
    }

    const validParams = { ...params, ...dynamicParams };
    const res =
      props == null
        ? await wsApi.createArray(baseID, validParams, preview)
        : await wsApi.setParams(props.dxid, validParams, preview);

    if (preview) {
      await previewInCommand(res);
    }

    return res;
  };

  const checkAndFinish = async () => {
    if (!isFormDataValid()) {
      return;
    }

    await execute();

    exit();
  };

  const exit = () => {
    previewer.clear();
    onClose();
  };

  // =================
  const { initEditDependency, previewInCommand } = useCommand('array-circle', {
    onClose,
    finishDraw: checkAndFinish,
  });

  const schema = useMemo(() => {
    const baseProperties = {
      base: {
        type: 'string',
        format: FeatureManager.isFeatureEnabled('array-for-hole')
          ? PickInputRJSFFormat.PickVertexOrLineOrFaceOrModelOrArray
          : composeFormat([
              BasePickInputRJSFFormat.PickVertex,
              BasePickInputRJSFFormat.PickModel,
              BasePickInputRJSFFormat.PickPartArray,
            ]),
      },
      axis: {
        type: 'string',
        format: composeFormat([
          PickInputRJSFFormat.PickLine,
          PickInputRJSFFormat.PickSurface,
        ]),
      },
      mode: {
        type: 'string',
        title: '方向',
        default: 'circle',
        oneOf: [
          {
            type: 'string',
            title: '单向',
            const: 'circle',
          },
          {
            type: 'string',
            title: '双向',
            const: 'doubleCircle',
          },
        ],
      },
    };

    if (formData.mode === 'circle') {
      const schemaForCircle = {
        properties: {
          ...baseProperties,
          array1: {
            type: 'object',
            properties: getArrayProperties(),
          },
          reverse: {
            type: 'boolean',
            default: false,
          },
        },
      };

      removeUnusedFieldForArray(formData, schemaForCircle, 'array1');

      return schemaForCircle as RJSFSchema;
    } else {
      const schemaForDoubleCircle = {
        properties: {
          ...baseProperties,
          array1: {
            type: 'object',
            title: '方向1',
            properties: getArrayProperties(),
          },
          array2: {
            type: 'object',
            title: '方向2',
            properties: getArrayProperties(),
          },
          equal: {
            type: 'boolean',
            title: '双向相同',
            default: true,
          },
        },
      };

      removeUnusedFieldForArray(formData, schemaForDoubleCircle, 'array1');

      if (formData.equal) {
        delete schemaForDoubleCircle.properties.array2;
      } else {
        removeUnusedFieldForArray(formData, schemaForDoubleCircle, 'array2');
      }

      return schemaForDoubleCircle as RJSFSchema;
    }
  }, [
    formData.mode,
    formData.array1.angleMode,
    formData.array2.angleMode,
    formData.equal,
  ]);

  return (
    <PickableCommandForm
      isFormDataValid={finishValid}
      disabled={false}
      onClose={exit}
      title={title}
      placement={placement}
      schema={schema}
      formData={formData}
      widgets={{
        reverseIcon: ReverseIconWidget,
      }}
      onChange={(v) => {
        setFormData(v);
      }}
      extraUiSchema={{
        mode: {
          'ui:widget': 'radio',
          'ui:classNames': 'command-form__mode-switch',
        },
        base: {
          'ui:placeholder': '请输入目标模型',
          'ui:autofocus': true,
        },
        axis: {
          'ui:placeholder': '请输入阵列轴',
        },
        array1: {
          'ui:label': formData.mode === 'doubleCircle',
          angleMode: {
            'ui:widget': 'radio',
            'ui:classNames': 'command-form__mode-switch',
          },
        },
        reverse: {
          'ui:classNames': 'array-circle-form-reverse-direction',
          'ui:widget': 'reverseIcon',
          'ui:label': false,
        },
        array2: {
          'ui:label': formData.mode === 'doubleCircle',
          angleMode: {
            'ui:widget': 'radio',
            'ui:classNames': 'command-form__mode-switch',
          },
        },
      }}
      submitText='完成'
      onSubmit={checkAndFinish}
    />
  );
}

export const cmdCircularArray: BaseCommand = {
  id: 'cmdCircularArray',
  label: 'command.array-circle',
  icon: 'commands/feature/array-circle',
  create: (label, onClose, props) => (
    <CircularArray
      title={i18n.t(label)}
      onClose={onClose}
      props={props}
    />
  ),
};
